/**
 * @class   wosh::SharedMemory
 * @brief   Base class of Shared Memory components, it provides a basic implementation
 *          of a custom leader-election algorithm and I/O methods.
 *
 * Note that most of the provided implementation are \c virtual and may be replaced or
 * extended by inherited classes, no support for replica management (dataset) is provided.
 *
 * SharedMemory components cooperate only by passing messages (on wosh.Bus.DSM),
 * in other words they work exactly the same anywhere (on same or different host),
 * indeed the network/framework cost is higher when agents are spread on the network.
 * 
 * On creation an unique ID is assigned to the agent, during setup the cunsumer will
 * set the tuplespace name (setName), agents cooperate only within same tuplespace.
 *
 * Most important goals of WOSH's SharedMemory components are:
 *  - transparent, elegant but flexible interaction with consumer
 *  - minimize network cost, but with different level of consistency
 *    (that's why there are more implementations as inherited classes)
 *
 *
 * Once configured (mode, strategy, propagation, ..), agent might be \ref page_sharedmemory_init initialized
 * and do its job; later, it will be very important to ref page_sharedmemory_deinit de-initialize the agent.
 *
 * Shared Memory is currently used by DeviceManager (as SharedMemoryEpidemic),
 * and (soon to be) by UserManager (as SharedMemoryDSM).
 *
 *
 * \section page_sharedmemory_assumptions Assumptions
 *
 *  - Network link (messaging) is reliable, but hosts and agents may disconnect anytime.
 *  - We are notified of remote host (dis)connection (thanks to NetworkManager events).
 *  - Broadcast messages are handled by the networking overlay, we assume all hosts of the network
 *    will be reached (unordered, at-least-one).
 *  - 'primary' flag is evaluated only against LOCAL list, since it's aligned each time we update the list.
 *
 * \note WOSH supports transient messaging, actually we can't assert links are reliable (in TCL world
 *       we almost always say "they are reliable enought").
 *
 *
 * \section page_sharedmemory_init Initialization
 *
 *  - Reset \c primary (true) and \c lastSeen (0) fields, set the \c kernel field
 *  - Clear the agent list
 *  - Connect to the \c wosh.Bus.DSM and \c wosh.Bus.Core busses
 *  - Raises an election (claiming as primary)
 *
 * \section page_sharedmemory_deinit Deinitialization
 *
 *  - Broadcast death 
 *  - Disconnect busses
 *
 *
 * \section page_sharedmemory_election Leader Election and List synchronization
 *
 * SharedMemory provides a default implemetation to handle <tt>group view<tt> and
 * elect a coordinator among live agents.
 *
 * Each agent maintains a set of information about other agents (the group view),
 * this list is required by many features such as:
 *  - leader election (have all agents agree on an unique primary agent)
 *  - crash/death detection
 *  - pull/push methods against a specific agent (usually the primary)
 *
 * Information about agents are stored as SharedMemoryInfo objects, it holds
 * most important SharedMemory fields (id, mode, ..) and also the local-kernel name
 * of the agent which enables some optimizations and the crash/death detection.
 *
 * Election flow is inspired to <tt>Bully Algorithm<tt>, applying some optimizations
 * for the WOSH environment.
 *
 * Leader is defined as the agent with oldest (smaller) seniority among the group.
 * Conceptually, primary agent should be as stable and reiliable as possible, using ID
 * as primary key introduces entropy and potetially each new agent may cause a new result.
 * In a WOSH network, there are different kind of hosts: some may be classified as servers,
 * others as clients (which appears and disappear, as a Mobile phone), it is resonable to
 * say the the oldest (initialized) host is more stable and should be considered as default primary.
 * Seniority field is a \c double value, where the integer part is the local timestamp (epoch)
 * of agent's creation and the real part is a random integer number (to avoid confilcts).
 *
 * Since leader election process is used also to update information about other agents,
 * the effincency in terms of transmitted bytes (an important point for such algorithms)
 * is not considered the first priority, instead we like to have a good consistency.
 *
 * 
 * Election \b Flow 
 *
 *  - On: Initialization
 *      Agent assumes to be the primary (setting lastSeen=0, it will know about guessing it)
 *      Once busses are connected, the agent raise an election-REQUEST
 *
 *  - On: DeInitialization
 *      Agent announces its death broadcasting an election-RESPONSE, the agents' list
 *      misses itself (entry), XXXXXXXXXXXXXXX
 *
 *  - On: Remote Host connected [onWoshHostConnectionChanged(,1,)]
 *      The remote WOSH kernel (which now is connected) may host agents (even primary), 
 *      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 *
 *  - On: Remote Host disconnected [onWoshHostConnectionChanged(,2,)]
 *      The remote WOSH kernel (which now is disconnected) may host agents (even primary),
 *      some different use-cases are evaluated to reduce flooding, but guarrantee consistency.
 *      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 *
 *  - Call: Election Request [SharedMemory::doElectionRequest()]
 *     - ++clock
 *     - create list of known agents including myself [id, clock, mode, seniority, ..]
 *     - broadcast the Request containing agents' list
 *
 *  - Call: Death notification [SharedMemory::doAnnounceDeath()]
 *     - ++clock
 *     - create list of known agents \b excluding myself
 *     - broadcast the Response containing such agents' list
 *
 *  - On Received Election-Request [SharedMemory::onElectionReceivedRequest()]
 *     - ignore my own requests (filtered by busMessage())
 *     - if the request is broadcast, reply only when primary
 *     - else (=i'm target or primary):
 *        - reply Election-Response (broadcast when primary || conflicts detected)
 *
 *  - On Received Election-Response [SharedMemory::onElectionReceivedResponse()]
 *     - ignore my own responses
 *     - from primary
 *     - 
 *
 * References:
 *  - http://home.dei.polimi.it/fornacia/didattica/labsw0304/2004ElectionAlgorithms.pdf
 *
 ****************************************************************************
 * @version 0.8.499 $Id: SharedMemory.h 2781 2010-07-19 23:15:58Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_SharedMemory_H__
 #define __WOSH_Core_SharedMemory_H__

 #include <woshDefs.h>
 #include <core/Object.h>
 #include <core/SharedObject.h>
 #include <core/Fact.h>
 #include <core/BusConnector.h>
 #include <core/LogContext.h>
 #include <core/PropertiesProvider.h>
 #include <core/MethodsProvider.h>


namespace wosh {

template <class T> class DataFieldCollector;


class SharedMemoryInfo : public virtual ISerializable
 {
	WOSH_SERIALIZABLE(wosh::SharedMemoryInfo)
	WOSH_COMPARE_RTTI(wosh::SharedMemoryInfo)

	public:
		SharedMemoryInfo()
			: id(0), seniority(0), clock(0), primary(false), mode(0), strategy(0), propagation(0), lastSeen(0) { }
		SharedMemoryInfo(const SharedMemoryInfo& m) {
			*this = m;
		 }
		SharedMemoryInfo& operator=(const SharedMemoryInfo& m);
		bool operator==(const SharedMemoryInfo& other) const;
		bool operator!=(const SharedMemoryInfo& other) const	{ return !(*this == other);  }

	public:
		inline long getID() const						{ return this->id; }
		inline const std::string& getKernel() const		{ return this->kernel; }
		inline double getSeniority() const				{ return this->seniority; }
		inline int64 getClock() const					{ return this->clock; }
		inline bool isPrimary() const					{ return this->primary; }
		inline short getMode() const					{ return this->mode; }
		inline short getStrategy() const				{ return this->strategy; }
		inline short getPropagation() const				{ return this->propagation; }
		inline long getLastSeen() const					{ return this->lastSeen; }

		static DataFieldCollector<SharedMemoryInfo>* getDataFields();

	public:
		std::string toString() const;
		bool updateHash( DataHashContext* hash_context ) const;

	public: // serialized and used within I/O
		long id;
		std::string kernel;
		double seniority;
		int64 clock;
		bool primary;
		short mode;
		short strategy;
		short propagation;
		long lastSeen;

	public: // NOT serialized and used within single instance (as flags, temp values, ..)
	//	bool died;

}; // class def



class ISharedMemoryListener {
	public:
		virtual bool sm_ObjectAdding( SharedObject* object ) = 0;
		virtual bool sm_ObjectUpdating( SharedObject* object ) = 0;
		virtual bool sm_ObjectDeleting( SharedObject* object ) = 0;

	public:
		virtual ~ISharedMemoryListener() { }
}; // class def



 #define _SharedMemory_DEFAULT_NAME						"SharedMemory"

 #define _SharedMemory_KEY_ID							"ID"
 #define _SharedMemory_KEY_Seniority					"Seniority"
 #define _SharedMemory_KEY_Clock						"Clock"
 #define _SharedMemory_KEY_Primary						"Primary"
 #define _SharedMemory_KEY_Mode							"Mode"
 #define _SharedMemory_KEY_Strategy						"Strategy"
 #define _SharedMemory_KEY_Propagation					"Propagation"

 #define _SharedMemory_KEY_PeriodAutoElection			"PeriodAutoElection"
 #define _SharedMemory_KEY_PeriodAutoDataSynch			"PeriodAutoDataSynch"
 #define _SharedMemory_KEY_StatsAgentsCount				"StatsAgentsCount"
 #define _SharedMemory_KEY_StatsDataSetCount			"StatsDataSetCount"

 #define _SharedMemory_METHOD_retrieve					"retrieve"
 #define _SharedMemory_METHOD_election					"election"
 #define _SharedMemory_METHOD_get_agents				"get_agents"
 #define _SharedMemory_METHOD_get_info					"get_info"
 #define _SharedMemory_METHOD_get_dataset				"get_dataset"
 #define _SharedMemory_METHOD_raise_election			"raise_election"

 #define _SharedMemory_EVENT_ObjectInvalidation			"ObjectInvalidation"
 #define _SharedMemory_EVENT_ObjectDeathCertificate		"ObjectDeathCertificate"


 #define SM_AgentsConflicts_VALID						0x0000
 #define SM_AgentsConflicts_PRIMARY_MULTIPLE			0x0001
 #define SM_AgentsConflicts_PRIMARY_NOT_OLDEST			0x0002
 #define SM_AgentsConflicts_CLOCK_IM_IN_FUTURE			0x0004
 #define SM_AgentsConflicts_CLOCK_SENDER_IN_PAST		0x0008

 #define SM_AgentsUpdateFlag_NONE						0x0000
 #define SM_AgentsUpdateFlag_UPDATE						0x0001
 #define SM_AgentsUpdateFlag_ADD_SENDER					0x0002
 #define SM_AgentsUpdateFlag_ADD_BUT_SENDER				0x0004
 #define SM_AgentsUpdateFlag_ADD_ALL					0x0006


typedef MapT<long, SharedMemoryInfo*> tLongSharedMemoryInfoMap;
typedef MapT<std::string, SharedObject*> tStrSharedObjectMap;
template <class T, class Y> class ThreadMethodPeriodicT;


class SharedMemory :	public Object,
						public virtual IPropertiesProviderListener,
						public virtual IBusListener,
						public MethodRunner
 {
	WOSH_CLASSNAME(wosh::SharedMemory)
	WOSH_CLASS_ABSTRACT(true)


	public:
		// define the role of an agent, most implementations force it to MODE_ADHOC
		enum AGENT_MODE {
			MODE_UNKNOWN			= 0x00,	///< undefined, for initialization
			MODE_CLIENT				= 0x01,	///< pull-based approach (= agent is a dummy client and WON'T spread ANY update!)
			MODE_SERVER				= 0x02,	///< push-based approach (=agent is a dummy server and WILL ONLY spread updates!)
			MODE_ADHOC				= 0x03	///< client/server mode (mixed, agent is able to send and receive updates)
		 };
		// AGENT_STRATEGY enum is a classification for inherited implementations.
		// the way updates are exchanged depends on AGENT_MODE, UPDATE_PROPAGATION
		enum AGENT_STRATEGY {
			STRATEGY_UNKNOWN		= 0x00,	///< undefined, for initialization
			STRATEGY_EPIDEMIC		= 0x01,	///< an update propagation triggers another towards a different server
			STRATEGY_DSM			= 0x02	///< 
		 };
		// define how agents may spread updates
		enum UPDATE_PROPAGATION {
			PROPAGATION_UNKNOWN		= 0x00,	///< undefined, for initialization
			PROPAGATION_INVALIDATE	= 0x01,	///< just send a notification of the occurred update
			PROPAGATION_REPLICA		= 0x02,	///< send the updated object itself
			PROPAGATION_ACTIVE		= 0x04	///< send the operation+data to apply 
		 };

	public:
		/**
		 * @brief  Default constructor with initializers.
		 * @param name [in] name of the tuple space (as 'my_devices')
		 * @param parent [in] owner of the component
		 */
		SharedMemory( const std::string& name = "", Object* parent = NULL );
		/**
		 * @brief  Empty destructor.
		 */
		virtual ~SharedMemory();

		static ListT<SharedMemory*>& getInstances();

/** @name Flow control
 * @{
 ******************************************************************************/
	public:
		// connect to bus
		virtual WRESULT init( unsigned long timeout = LIMIT_ULONG_MAX );
		// TODO!? broadcast death certificate and
		// disconnect bus
		virtual WRESULT deinit( unsigned long timeout = LIMIT_ULONG_MAX );

		virtual void doElectionRequest();

//@}
/** @name Getters (reentrant)
 * @{
 ******************************************************************************/
	public:
		inline long getID() const					{ return this->info.id; }
		inline double getSeniority() const			{ return this->info.seniority; }
		long getSeniorityTime() const;
		long getSeniorityReal() const;
		int64 getClock() const						{ MutexLockerRead ml(this->agents.getMutex()); return this->info.clock; }

		bool isPrimary() const						{ MutexLockerRead ml(this->agents.getMutex()); return this->info.primary; }
		AGENT_MODE getMode() const					{ MutexLockerRead ml(this->agents.getMutex()); return (AGENT_MODE)this->info.mode; }
		AGENT_STRATEGY getStrategy() const			{ MutexLockerRead ml(this->agents.getMutex()); return (AGENT_STRATEGY)this->info.strategy; }
		UPDATE_PROPAGATION getPropagation() const	{ MutexLockerRead ml(this->agents.getMutex()); return (UPDATE_PROPAGATION)this->info.propagation; }
		SharedMemoryInfo getInfo() const			{ MutexLockerRead ml(this->agents.getMutex()); return this->info; }

		inline bool isModeClient() const			{ return (getMode() & MODE_CLIENT); }
		inline bool isModeServer() const			{ return (getMode() & MODE_SERVER); }

		inline const tLongSharedMemoryInfoMap& getAgents() const	{ return this->agents; }
		inline tLongSharedMemoryInfoMap& getAgents()				{ return this->agents; }

		inline BusConnector& getBusDsm()					{ return this->BusDSM; }
		inline LogContext& getLog()							{ return this->Log; }

		unsigned long getElectionPeriod() const;
		unsigned long getDataSynchPeriod() const;

		static DataFieldCollector<SharedMemory>* getDataFields();

		static const char* getAgentModeAsString( AGENT_MODE mode );
		static const char* getAgentStrategyAsString( AGENT_STRATEGY strategy );
		static const char* getUpdatePropagationAsString( UPDATE_PROPAGATION propagation );

//@}
/** @name Setters (reentrant)
 * @{
 ******************************************************************************/
	public:
		virtual void setName( const std::string& name );

		virtual WRESULT setMode( AGENT_MODE mode, bool raise_update = true ) = 0;
		virtual WRESULT setPropagation( UPDATE_PROPAGATION propagation, bool raise_update = false ) = 0;

		virtual WRESULT setElectionPeriodic( unsigned long period_seconds, unsigned long ctrl_timeout = LIMIT_ULONG_MAX );
		virtual WRESULT setDataSynchPeriodic( unsigned long period_seconds, unsigned long ctrl_timeout = LIMIT_ULONG_MAX );

//@}
/** @name Data manipulation
 * @{
 ******************************************************************************/
	public:
		const tStrSharedObjectMap& getDataSet() const	{ return this->dataset; }
		tStrSharedObjectMap& getDataSet()				{ return this->dataset; }

		template<class CONTAINER>//, class OBJECT>
		WRESULT acquireDataSet( CONTAINER& std_container ) {
			typename CONTAINER::Iterator it;
			const typename CONTAINER::ConstIterator it_end = std_container.end();
			for(it=std_container.begin(); it!=it_end; ++it) {
				this->dataset.set("asd", *it);
			 }
			return WRET_OK;
		 }

//@}
/** @name Message Events (incoming message)
 * @{
 ******************************************************************************/
	public:
		virtual void busMessage( const Message& message, const Bus* source );
//@}
/** @name IPropertiesProviderListener interface
 * @{
 ******************************************************************************/
 	public:
		virtual bool readingProperty( Property* property_curr, const PropertiesProvider* source );
		virtual bool updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source );
//@}
/** @name Internal events
 * @{
 ******************************************************************************/
	protected:
		virtual void busMessage_Core( const Message& message, const Bus* source );

		virtual void onElectionReceivedRequest( const List* response_agents, long sender_id, long target_id );
		virtual void onElectionReceivedResponse( const List& response_agents, long sender_id, long target_id );
		virtual void onWoshHostConnectionChanged( const std::string& hostname, int connection_state, const Message* message = NULL );

		virtual bool onAgentAdding( const SharedMemoryInfo& info, long sender_id, long target_id );
		virtual bool onAgentUpdating( const SharedMemoryInfo& info, long sender_id, long target_id );
		virtual bool onAgentRemoving( const SharedMemoryInfo& info, long sender_id, long target_id );

		virtual bool onAgentPrimaryChanged( const SharedMemoryInfo& primary_agent, long sender_id, long target_id );

//@}
	private: // methods and bindings, cannot be virtual.. so using following wrappers
		void mmDoElectionRequest()			{ doElectionRequest_(0); }
		void doElectionPeriodicUpdate()		{ doElectionRequest_(0); }
		void doDataSynchPeriodicUpdate()	{ ; } ///@todo code here

	protected:
		virtual void doElectionRequest_( long target_agent );
		virtual void doAnnounceDeath();

		int doElectionEvaluateAgentsOld_( const List& response_agents, long sender_id, long target_id );
		int doElectionEvaluateAgentsNew_( const List& response_agents, long sender_id, long target_id, int update_flags );
		int checkConflicts_( const List& container, long sender_id ) const;

		bool isPrimary_( long sender_id ) const;
		bool isDeathNotification( const List& response_agents, long sender_id, long target_id ) const;
		bool isNewAgent( const List& request_agents, long sender_id, long target_id ) const;
		long findPrimary_() const;
		long findPrimary( const List& container ) const;
		long fixPrimaryFlag_( long new_primary = 0 );
		long fixPrimaryFlag( List& container, long new_primary = 0 ) const;


	protected:
		virtual void sendToAgent( Message* message, long target_id );
		long selectRandomAgent_();

	protected:
		void onDataSetReceived( const List& response_dataset, const Message* message = NULL );
		List* copyDataSet() const;
		int mergeDataSet( const List& response_dataset );

	protected:
		SharedMemoryInfo info;					///< my own information/settings ( referenced also as agents[0] )
		tLongSharedMemoryInfoMap agents;		///< info about all known agents
		tStrSharedObjectMap dataset;			///< the (shared) tuple space
		BusConnector BusCore;					///< connector to core bus
		BusConnector BusDSM;					///< connector to our private bus
		mutable LogContext Log;					///< my logger
		mutable MethodsProvider Methods;		///< my methods
		mutable PropertiesProvider Properties;	///< my configuration

		ThreadMethodPeriodicT<SharedMemory, void>* methodPeriodicElection;	///< thread binded with doElectionPeriodicUpdate
		ThreadMethodPeriodicT<SharedMemory, void>* methodPeriodicDataSynch;	///< thread binded with doDataSynchPeriodicUpdate

	private:
		SharedMemory(const SharedMemory&) : Object() { } ///< private copy constructor (object non-copiable)
		SharedMemory& operator=(const SharedMemory&) { return *this; } ///< private assigment operator (object non-copiable)


}; // class def



class SharedMemory_ObjectInvalidation : public wosh::Fact
 {
	WOSH_SERIALIZABLE(wosh::SharedMemory_ObjectInvalidation)

	public:
		/**
		 * @brief  Empty constructor.
		 */
		SharedMemory_ObjectInvalidation()
			: wosh::Fact(_SharedMemory_EVENT_ObjectInvalidation) { }
		/**
		 * @brief  Constructor with initializers.
		 * @param obj_id [in] id of the (updated) object
		 */
		SharedMemory_ObjectInvalidation( const std::string& obj_id, SharedObject::OPERATION operation );

		SharedMemory_ObjectInvalidation( const std::string& obj_id );

		/**
		 * @brief  Copy constructor.
		 * @param m [in] the source object to be copied
		 */
		SharedMemory_ObjectInvalidation( const SharedMemory_ObjectInvalidation& m )
			: wosh::Fact(m) { }
		/**
		 * @brief  Empty destructor.
		 */
		virtual ~SharedMemory_ObjectInvalidation() { }

	public:
		std::string getObjectID() const;
		SharedObject::OPERATION getUpdateOperation() const;

}; // class def



}; // namespace wosh

#endif //__WOSH_Core_SharedMemory_H__
